﻿#include "pch.h"
#include "sbcs.h"
#include "sbhs.h"
#include "kstub_mutex.h"
#include "version.h"

// 외부
extern void _k_intl_init(void);
extern void _k_dbgout_init(void);
extern void _k_dbgout_disp(void);
extern void _k_mem_init(void);
extern void _k_mem_disp(void);
extern void _k_sym_init(void);
extern void _k_sym_disp(void);
extern void _k_si_init(void);
extern void _k_once_init(void);
extern void _k_once_disp(void);
extern void _k_thd_init(void);
extern void _k_thd_disp(void);


//////////////////////////////////////////////////////////////////////////
// 메인

// 닫기 구조체
struct kCclosure
{
	struct kCclosure*	prev;
	kanycb				data;
	kcham				zero;
};

// 구현
static struct kImplMain
{
	kcham				isinit;

	struct kCclosure*	closure;
	struct kCclosure*	presure;

	STUB_MUTEX			lock;
	STUB_MUTEX			unlk;

#if _SB_PTHREAD_
	pthread_mutexattr_t	pt_mutexattr;
	pthread_condattr_t	pt_condattr;
#endif
} k_impl_main =
{
	FALSE,

	NULL,
	NULL,

#if _SB_WINDOWS_
	{0, },
	{0, },
#endif

#if _SB_PTHREAD_
#if 0
	PTHREAD_MUTEX_INITIALIZER,
	PTHREAD_COND_INITIALIZER,
#else
	{0,},
	{0,},
#endif
#endif
};

#if _SB_PTHREAD_
pthread_mutexattr_t* k_mutexattr(void)
{
	return &k_impl_main.pt_mutexattr;
}

pthread_condattr_t* k_condattr(void)
{
	return &k_impl_main.pt_condattr;
}
#endif

//
static void _k_disp(void);

// 초기화
static void _k_init(void)
{
	k_impl_main.isinit = TRUE;

#if _SB_PTHREAD_
	// pthread 속성
	pthread_mutexattr_init(&k_impl_main.pt_mutexattr);
	pthread_mutexattr_settype(&k_impl_main.pt_mutexattr, PTHREAD_MUTEX_RECURSIVE);
	pthread_condattr_init(&k_impl_main.pt_condattr);
#endif

	// 전역 잠금
	stub_mutex_init(&k_impl_main.lock, 4000);
	stub_mutex_init(&k_impl_main.unlk, 4000);

	// 초기화
	_k_intl_init();		// 국제화
	_k_dbgout_init();	// 디버그 정보
	_k_si_init();		// 시스템 정보
	_k_mem_init();		// 메모리
	_k_sym_init();		// 심볼
	_k_thd_init();		// 스레드
	_k_once_init();		// 스레드 once

#if _SB_STATIC_
	atexit(_k_disp);
#endif

#if _SB_WINDOWS_
	if (k_candebug())
		OutputDebugStringA("[@sbcs]\n");
#endif
}

// 제거
static void _k_disp(void)
{
	struct kCclosure* node;
	struct kCclosure* prev;

	if (!k_impl_main.isinit)
		return;

	stub_mutex_enter(&k_impl_main.lock);

	// 클로저
	for (node = k_impl_main.closure; node; node = prev)
	{
		prev = node->prev;
		if (node->zero)
			((kfunc)node->data.func)();
		else
			((kfunc_1)node->data.func)(node->data.data);
		k_delete(node);
	}

	// 우선 순위 클로저
	for (node = k_impl_main.presure; node; node = prev)
	{
		prev = node->prev;
		((kfunc_1)node->data.func)(node->data.data);
		k_delete(node);
	}

	// 제거
	_k_once_disp();			// 스레드 once
	_k_thd_disp();			// 스레드
	_k_sym_disp();			// 심볼
	_k_mem_disp();			// 메모리
	_k_dbgout_disp();		// 디버그 정보

	stub_mutex_leave(&k_impl_main.lock);
	stub_mutex_disp(&k_impl_main.lock);
	stub_mutex_disp(&k_impl_main.unlk);

#if _SB_PTHREAD_
	pthread_mutexattr_destroy(&k_impl_main.pt_mutexattr);
	pthread_condattr_destroy(&k_impl_main.pt_condattr);
#endif

#if _SB_WINDOWS_ && _SB_DEBUG_
	// 테스트용 메모리 검사
	_CrtDumpMemoryLeaks();
#endif	

	//
	k_impl_main.isinit = FALSE;

#if _SB_WINDOWS_
	if (k_candebug())
		OutputDebugStringA("[~sbcs]\n");
#endif
}

/** CS 런타임의 초기화. */
void k_runtime(kcham use_type_library, kint v[])
{
#if _SB_STATIC_
	if (!k_impl_main.isinit)
		_k_init();
#endif

	if (v)
	{
		v[0] = SB_VER_MAJOR;
		v[1] = SB_VER_MINOR;
		v[2] = SB_VER_BUILD;
		v[3] = SB_VER_REVISION;
	}

	if (use_type_library)
		k_typelib();
}

/**
* CS 런타임 버전 정보.
* @param	v	값을 받을 4개 원소를 갖는 정수.
*/
void k_version(kint v[])
{
	k_return_if_fail(v != NULL);

	v[0] = SB_VER_MAJOR;
	v[1] = SB_VER_MINOR;
	v[2] = SB_VER_BUILD;
	v[3] = SB_VER_REVISION;
}

/** 전역 뮤텍스 잠금. */
void k_p_lock(void)
{
	stub_mutex_enter(&k_impl_main.lock);
}

/** 전역 뮤텍스 풀기. */
void k_p_unlock(void)
{
	stub_mutex_leave(&k_impl_main.lock);
}

/** 부분 뮤텍스 잠금. */
void k_u_lock(void)
{
	stub_mutex_enter(&k_impl_main.unlk);
}

/** 부분 뮤텍스 풀기. */
void k_u_unlock(void)
{
	stub_mutex_leave(&k_impl_main.unlk);
}

/**
 * 프로그램 끝내기.
 * @param	exitcode	끝내기 코드.
 */
void k_exit(kint exitcode)
{
#if _SB_WINDOWS_DESKTOP_
	ExitProcess((UINT)exitcode);
#else
	exit(exitcode);
#endif
}

/**
 * 종료할 때 호출할 콜백 등록.
 * @param	cb			콜백 함수.
 * @param	userdata	사용자 데이터.
 */
void k_atexit(kfunc_1 cb, kpointer userdata)
{
	struct kCclosure* node;

	if (cb)
	{
		k_p_lock();

		node = k_new_1(struct kCclosure);
		node->zero = FALSE;
		node->data.func = (kfunc)cb;
		node->data.data = userdata;
		node->prev = k_impl_main.closure;
		k_impl_main.closure = node;

		k_p_unlock();
	}
}

/**
 * 종료할 때 호출할 콜백 등록(사용자 데이터 없음)
 * @param	cb	콜백 함수.
 */
void k_atexit_0(kfunc cb)
{
	struct kCclosure* node;

	if (cb)
	{
		k_p_lock();

		node = k_new_1(struct kCclosure);
		node->zero = TRUE;
		node->data.func = cb;
		node->data.data = NULL;
		node->prev = k_impl_main.closure;
		k_impl_main.closure = node;

		k_p_unlock();
	}
}

/**
 * 종료할 때 호출할 콜백 등록(우선순위).
 * @param	cb			콜백 함수.
 * @param	userdata	사용자 데이터.
 */
void k_atexit_p(kfunc_1 cb, kpointer userdata)
{
	struct kCclosure* node;

	if (cb)
	{
		k_p_lock();

		node = k_new_1(struct kCclosure);
		node->zero = FALSE;
		node->data.func = (kfunc)cb;
		node->data.data = userdata;
		node->prev = k_impl_main.presure;
		k_impl_main.presure = node;

		k_p_unlock();
	}
}


//////////////////////////////////////////////////////////////////////////
// 메인
#if !_SB_STATIC_
#if _SB_WINDOWS_

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
	switch (ul_reason_for_call)
	{
		case DLL_PROCESS_ATTACH:
			_k_init();
			break;

		case DLL_THREAD_ATTACH:
		case DLL_THREAD_DETACH:
			break;

		case DLL_PROCESS_DETACH:
			_k_disp();
			break;
	}

	hModule;
	lpReserved;

	return TRUE;
}

#else

void __attribute__((constructor)) k_attach(void)
{
	// 정적 라이브러리
	// : -Wl,-whole-archive libmodules.a -Wl,-no-whole-archive
	//
	// NDK
	// : LOCAL_WHOLE_STATIC_LIBRARIES := modules
	// : $(BUILD_EXECUTABLE) 또는 $(BUILD_SHARED_LIBRARY)
	// : #LOCAL_LDFLAGS := -Wl,-whole-archive $(라이브러리_경로)/libmodules.a -Wl,-no-whole-archive
	_k_init();
}

void __attribute__((destructor)) k_detach(void)
{
	_k_disp();
}

#endif	// _SB_WINDOWS_
#endif	// !_SB_STATIC_
